package com.empire.module.member.service.warningindividual;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.empire.framework.common.enums.WarnTypeEnum;
import com.empire.module.member.dal.dataobject.mapconfig.MapConfigDO;
import com.empire.module.member.dal.dataobject.presentation.PresentationDO;
import com.empire.module.member.dal.dataobject.tag.MemberTagDO;
import com.empire.module.member.dal.dataobject.transportationrecord.TransportationRecordDO;
import com.empire.module.member.dal.dataobject.user.MemberUserDO;
import com.empire.module.member.dal.dataobject.warninggather.WarningGatherDO;
import com.empire.module.member.dal.dataobject.warningindividual.PersonWarningCountDTO;
import com.empire.module.member.dal.dataobject.warningindividual.UnifiedWarningDTO;
import com.empire.module.member.dal.dataobject.warningindividual.WarningTypeCountDTO;
import com.empire.module.member.dal.mysql.mapconfig.MapConfigMapper;
import com.empire.module.member.dal.mysql.user.MemberUserMapper;
import com.empire.module.member.dal.mysql.warninggather.WarningGatherMapper;
import com.empire.module.member.service.casepersonrelation.CasePersonRelationService;
import com.empire.module.member.service.presentation.PresentationService;
import com.empire.module.member.service.presentationpersonrelation.PresentationPersonRelationService;
import com.empire.module.member.service.tag.MemberTagService;
import com.empire.module.member.service.user.MemberUserService;
import com.empire.module.system.api.user.AdminUserApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import com.empire.module.member.controller.admin.warningindividual.vo.*;
import com.empire.module.member.dal.dataobject.warningindividual.WarningIndividualDO;
import com.empire.framework.common.pojo.PageResult;
import com.empire.framework.common.util.object.BeanUtils;

import com.empire.module.member.dal.mysql.warningindividual.WarningIndividualMapper;

import javax.annotation.Resource;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.empire.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.empire.module.member.enums.ErrorCodeConstants.*;

/**
 * 非聚集预警记录表（1条记录对应1个人的1次预警） Service 实现类
 *
 * @author 主席大人
 */
@Slf4j
@Service
@Validated
public class WarningIndividualServiceImpl extends ServiceImpl<WarningIndividualMapper, WarningIndividualDO> implements WarningIndividualService {

    @Resource
    private WarningIndividualMapper warningIndividualMapper;
    @Resource
    private MemberUserMapper memberUserMapper;
    @Resource
    private MemberTagService memberTagService;
    @Resource
    private MemberUserService memberUserService;
    @Resource
    private CasePersonRelationService casePersonRelationService;
    @Resource
    private PresentationService presentationService;
    @Resource
    private PresentationPersonRelationService  presentationPersonRelationService;
    @Resource
    private WarningGatherMapper gatherMapper;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MapConfigMapper mapConfigMapper;

    // 线程池，用于并行处理分批次查询
    private final ExecutorService executorService = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() + 1);

    @Override
    public Long createWarningIndividual(WarningIndividualSaveReqVO createReqVO) {
        // 插入
        WarningIndividualDO warningIndividual = BeanUtils.toBean(createReqVO, WarningIndividualDO.class);
        warningIndividualMapper.insert(warningIndividual);
        // 返回
        return warningIndividual.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createTrasportWarningIndividual(TransportationRecordDO transportationRecord) {
        // 1. 参数校验
        if (transportationRecord == null) {
            log.warn("创建交通创建交通预警失败：交通记录为空");
            return 0L;
        }
        Long personId = transportationRecord.getPersonId();
        if (personId == null || personId <= 0) {
            log.warn("创建交通预警失败：人员ID无效");
            return 0L;
        }

        // 2. 获取出发地和目的地，均为空则无需处理
        String startLocation = StrUtil.trimToEmpty(transportationRecord.getStartLocation());
        String endLocation = StrUtil.trimToEmpty(transportationRecord.getEndLocation());
        if (StrUtil.isEmpty(startLocation) && StrUtil.isEmpty(endLocation)) {
            log.debug("出发地和目的地均为空，不创建预警");
            return 0L;
        }

        // 3. 查询出入杭预警配置（修正查询条件对象）
        LambdaQueryWrapper<MapConfigDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MapConfigDO::getWarnType, WarnTypeEnum.IN_OR_OUT_HZ.getValue());
        List<MapConfigDO> mapConfigs = mapConfigMapper.selectList(queryWrapper);
        if (mapConfigs.isEmpty()) {
            log.debug("未查询到出入杭预警配置，不创建预警");
            return 0L;
        }

        // 4. 处理预警时间（避免空则使用当前时间）
        LocalDateTime warningTime = transportationRecord.getCheckintime();
        if (warningTime == null) {
            warningTime = LocalDateTime.now();
            log.warn("交通记录检票时间为空，使用当前时间作为预警时间，人员ID：{}", personId);
        }
        String checkTime = LocalDateTimeUtil.format(warningTime, DatePattern.NORM_DATETIME_PATTERN);

        int createCount = 0;
        // 5. 遍历配置，判断断是否触发预警
        for (MapConfigDO config : mapConfigs) {
            String keywordStr = StrUtil.trimToEmpty(config.getKeywords());
            if (StrUtil.isEmpty(keywordStr)) {
                log.debug("预警配置关键词为空，跳过处理，配置ID：{}", config.getId());
                continue;
            }

            // 处理关键词：中文逗号转英文，分割后过滤空字符串
            String[] keywords = StrUtil.split(keywordStr.replaceAll("，", ","), ",").toArray(new String[0]);
            keywords = Arrays.stream(keywords)
                    .map(StrUtil::trimToEmpty)
                    .filter(StrUtil::isNotEmpty)
                    .toArray(String[]::new);

            if (keywords.length == 0) {
                log.debug("预警配置关键词处理后为空，跳过处理，配置ID：{}", config.getId());
                continue;
            }

            // 6. 判断出发地是否触发离杭预警
            String content1 = checkStartLocationTrigger(startLocation, keywords, checkTime);
            // 7. 判断目的地是否触发入杭预警（如果出发地未触发）
            String content2 = checkEndLocationTrigger(endLocation, keywords, checkTime);
            String content;
            if (StrUtil.isNotEmpty(content1) && StrUtil.isNotEmpty(content2)) {
                // 说明是市内不需要预警
                content = "";
            }else {
                if (StrUtil.isNotEmpty(content1)) {
                    content = content1;
                } else {
                    content = content2;
                    // 针对外国人记录入杭次数，插入入杭次数标签
                    MemberUserDO person = memberUserService.getUser(personId);
                    if (person.getForeigner() == 1) {
                        Integer inTimeNumber = person.getInHangZhouTagTimeNumber();
                        if (inTimeNumber==null) {
                            inTimeNumber = 1;
                        }else {
                            inTimeNumber = inTimeNumber + 1;
                        }
                        String inTimeNumberStr = inTimeNumber + "次入杭";
                        // 看看是否新增标签
                        List<Long> tagIds = person.getTagIds();

                        LambdaQueryWrapper<MemberTagDO> queryWrapperTag = new LambdaQueryWrapper<>();
                        queryWrapperTag.eq(MemberTagDO::getName, inTimeNumberStr);
                        if (memberTagService.getOne(queryWrapperTag) == null) {
                            MemberTagDO tag = new MemberTagDO();
                            tag.setName(inTimeNumberStr);
                            memberTagService.save(tag);
                            Long tagId = tag.getId();
                            if (!tagIds.contains(tagId)) {
                                tagIds.add(tagId);
                            }
                        }

                        person.setInHangZhouTagTimeNumber(inTimeNumber);
                        person.setInHangZhouTagTime(inTimeNumberStr);
                        person.setTagIds(tagIds);
                        memberUserMapper.updateById(person);
                    }
                }
            }
            // 8. 触发预警，创建记录
            if (StrUtil.isNotEmpty(content)) {
                // 检查是否已存在相同预警，避免重复创建
                if (isDuplicateWarning(personId, config.getId(), warningTime)) {
                    log.debug("已存在相同预警记录，跳过创建，人员ID：{}，配置ID：{}", personId, config.getId());
                    continue;
                }

                // 循环内创建新对象，避免属性污染
                WarningIndividualDO individualDO = WarningIndividualDO.builder()
                        .personId(personId)
                        .createTime(LocalDateTime.now())
                        .warningTime(warningTime)
                        .warnType(WarnTypeEnum.IN_OR_OUT_HZ.getValue())
                        .warningConditionId(config.getId()) // 确保ID格式正确
                        .warningContent(content)
                        .isRead(0) // 默认为未读
                        .build();

                warningIndividualMapper.insert(individualDO);
                createCount++;
                log.info("创建交通预警成功，ID：{}，人员ID：{}，内容：{}",
                        individualDO.getId(), personId, content);
            }
        }

        log.info("交通预警处理完成，人员ID：{}，创建预警数量：{}", personId, createCount);
        return (long) createCount;
    }

    /**
     * 检查出发地是否触发离杭预警
     */
    private String checkStartLocationTrigger(String startLocation, String[] keywords, String checkTime) {
        if (StrUtil.isEmpty(startLocation)) {
            return null;
        }
        for (String keyword : keywords) {
            if (startLocation.contains(keyword)) {
                return String.format("【%s】始发站【%s】触发离杭预警", checkTime, startLocation);
            }
        }
        return null;
    }

    /**
     * 检查目的地是否触发入杭预警
     */
    private String checkEndLocationTrigger(String endLocation, String[] keywords, String checkTime) {
        if (StrUtil.isEmpty(endLocation)) {
            return null;
        }
        for (String keyword : keywords) {
            if (endLocation.contains(keyword)) {
                // 入杭次数记录

                // 修复文案错误：将"始发站"改为"终点站"
                return String.format("【%s】终点站【%s】触发入杭预警", checkTime, endLocation);
            }
        }
        return null;
    }

    /**
     * 检查是否存在重复预警
     * 避免同一人员在同一时间因同一配置重复触发预警
     */
    private boolean isDuplicateWarning(Long personId, String configId, LocalDateTime warningTime) {
        // 时间范围：前后1分钟内视为同一时间（避免毫秒级差异）
        LocalDateTime startTime = warningTime.minusMinutes(1);
        LocalDateTime endTime = warningTime.plusMinutes(1);

        LambdaQueryWrapper<WarningIndividualDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WarningIndividualDO::getPersonId, personId)
                .eq(WarningIndividualDO::getWarningConditionId, configId)
                .eq(WarningIndividualDO::getWarnType, WarnTypeEnum.IN_OR_OUT_HZ.getValue())
                .between(WarningIndividualDO::getWarningTime, startTime, endTime);

        return warningIndividualMapper.exists(queryWrapper);
    }

    @Override
    public void updateWarningIndividual(WarningIndividualSaveReqVO updateReqVO) {
        // 校验存在
        validateWarningIndividualExists(updateReqVO.getId());
        // 更新
        WarningIndividualDO updateObj = BeanUtils.toBean(updateReqVO, WarningIndividualDO.class);
        warningIndividualMapper.updateById(updateObj);
    }

    @Override
    public void deleteWarningIndividual(Long id) {
        // 校验存在
        validateWarningIndividualExists(id);
        // 删除
        warningIndividualMapper.deleteById(id);
    }

    private void validateWarningIndividualExists(Long id) {
        if (warningIndividualMapper.selectById(id) == null) {
            throw exception(WARNING_INDIVIDUAL_NOT_EXISTS);
        }
    }

    @Override
    public WarningIndividualDO getWarningIndividual(Long id) {
        return warningIndividualMapper.selectById(id);
    }

    @Override
    public PageResult<WarningIndividualDO> getWarningIndividualPage(WarningIndividualPageReqVO pageReqVO) {
        Long personId = pageReqVO.getPersonId();
        if (personId != null) {
            pageReqVO.setPersonId(personId);
        }else {
            List<Long> personIdList = memberUserMapper.selectByIdNumber(pageReqVO.getIdNumber());
            pageReqVO.setPersonId(personIdList != null && !personIdList.isEmpty() ? personIdList.getFirst() : null);
        }
        return warningIndividualMapper.selectPage(pageReqVO);
    }

    @Override
    public List<PersonWarningCountDTO> countPersonWarnings(List<Integer> personIdList) {
        return warningIndividualMapper.countPersonWarnings(personIdList);
    }

    /**
     * 单条标记已读：更新is_read为1
     */
    @Override
    public void markSingleRead(Long id) {
        // 1. 校验记录是否存在
        WarningIndividualDO personDO = warningIndividualMapper.selectById(id);
        if (personDO == null) {
            throw exception(WARNING_GATHER_PERSON_NOT_EXISTS); // 需自定义"记录不存在"的错误码
        }
        // 2. 已读无需重复更新
        if (personDO.getIsRead() == 1) {
            return;
        }
        // 3. 更新is_read字段
        LambdaUpdateWrapper<WarningIndividualDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(WarningIndividualDO::getId, id)
                .set(WarningIndividualDO::getIsRead, 1); // 标记为已读
        warningIndividualMapper.update(null, updateWrapper);
    }

    /**
     * 批量标记已读：批量更新is_read为1（事务保证原子性）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void markBatchRead(List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return;
        }
        // 批量更新：仅更新未读的记录（is_read=0）
        LambdaUpdateWrapper<WarningIndividualDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(WarningIndividualDO::getId, ids)
                .eq(WarningIndividualDO::getIsRead, 0) // 只更新未读的
                .set(WarningIndividualDO::getIsRead, 1); // 标记为已读
        warningIndividualMapper.update(null, updateWrapper);
    }

    @Override
    public PageResult<UnifiedWarningDTO> getCaseLatestWarnPage(LatestWarnPageReqVO pageVO) {
        PageResult<UnifiedWarningDTO> result = new PageResult<>(new ArrayList<>(), 0L);
        // 验证参数
        if (pageVO.getCaseId() == null) {
            return result;
        }

        // 获取案件相关人员ID
        Set<Long> personIdSet = casePersonRelationService.getCasePersonRelation(pageVO.getCaseId());
        if (personIdSet.isEmpty()) {
            return result;
        }

        List<Long> personIdList = new ArrayList<>(personIdSet);
        int batchSize = 500; // 每批次处理500个ID

        try {
            // 如果人员数量较少，直接查询（使用存储过程的分页和总条数）
            if (personIdList.size() <= batchSize) {
                String personIds = personIdList.stream()
                        .map(String::valueOf)
                        .collect(Collectors.joining(","));
                int offset = (pageVO.getPageNo() - 1) * pageVO.getPageSize();

                // 调用存储过程查询（带总条数）
                PageResult<UnifiedWarningDTO> personListPage = getLatestWarnings(
                        pageVO.getPageSize(), offset, personIds);
                List<UnifiedWarningDTO> personList = personListPage.getList();

                // 补充人员信息和聚集预警数据
                supplementWarningInfo(personList);

                // 正确返回包含总条数的PageResult
                return personListPage;
            }

            // 人员数量较多，分批次查询并合并结果（含总条数计算）
            return queryByBatches(personIdList, pageVO, batchSize);
        } catch (Exception e) {
            throw new RuntimeException("查询预警信息失败", e);
        }
    }

    @Override
    public PageResult<UnifiedWarningDTO> getHomeLatestWarnPage(LatestWarnPageReqVO pageVO) {
        PageResult<UnifiedWarningDTO> result = new PageResult<>(new ArrayList<>(), 0L);
        List<Long> personIdList = memberUserService.getMyUserIdList();
        if (!adminUserApi.isAdmin() && personIdList.isEmpty()) {
            // 不是管理员 myUserIdList 必须有值
            return result;
        }
        // 每批次处理500个ID
        int batchSize = 500;
        try {
            // 如果人员数量较少，直接查询（使用存储过程的分页和总条数）
            if (personIdList.size() <= batchSize) {
                String personIds = personIdList.stream()
                        .map(String::valueOf)
                        .collect(Collectors.joining(","));
                int offset = (pageVO.getPageNo() - 1) * pageVO.getPageSize();

                // 调用存储过程查询（带总条数）
                PageResult<UnifiedWarningDTO> personListPage = getLatestWarnings(
                        pageVO.getPageSize(), offset, personIds);
                List<UnifiedWarningDTO> personList = personListPage.getList();

                // 补充人员信息和聚集预警数据
                supplementWarningInfo(personList);

                // 正确返回包含总条数的PageResult
                return personListPage;
            }

            // 人员数量较多，分批次查询并合并结果（含总条数计算）
            return queryByBatches(personIdList, pageVO, batchSize);
        } catch (Exception e) {
            throw new RuntimeException("查询预警信息失败", e);
        }
    }

    @Override
    public PageResult<UnifiedWarningDTO> getPersonLatestWarnPage(LatestWarnPageReqVO pageVO) {
        PageResult<UnifiedWarningDTO> result = new PageResult<>(new ArrayList<>(), 0L);
        if(pageVO.getPersonId() == null){
            return result;
        }
        int offset = (pageVO.getPageNo() - 1) * pageVO.getPageSize();
        return getPersonLatestWarnings(pageVO.getPageSize(), offset, pageVO.getPersonId(), pageVO.getWarnType());
    }

    @Override
    public PageResult<UnifiedWarningDTO> presentationLatestWarnPage(Long presentationId) {
        PresentationDO presentationDO = presentationService.getById(presentationId);
        Date startTime = presentationDO.getStartTime();
        Date endTime = presentationDO.getEndTime();
        Set<Long> personIdSet = presentationPersonRelationService.getPresentationPersonIds(presentationId);

        return null;
    }

    public PageResult<UnifiedWarningDTO> getLatestWarnings(Integer pageSize, Integer offset, String personIds) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("pageSize", pageSize);
        paramMap.put("offset", offset);
        paramMap.put("personIds", personIds);

        // 调用存储过程（返回数据列表，总条数通过out参数返回）
        List<UnifiedWarningDTO> warningList = warningIndividualMapper.selectLatestWarn(paramMap);

        // 补充人员信息和聚集预警数据
        supplementWarningInfo(warningList);

        // 注意：存储过程返回的totalCount是Integer，需要转为Long
        Integer totalInt = (Integer) paramMap.get("totalCount");
        Long totalCount = totalInt != null ? totalInt.longValue() : 0L;

        return new PageResult<>(warningList, totalCount);
    }

    /**
     * 查询个人预警列表
     *
     * @param pageSize  每页显示条数
     * @param offset    偏移量
     * @param personId  待查询人员Id
     * @param warnType  预警类型
     * @return 预警列表
     */
    public PageResult<UnifiedWarningDTO> getPersonLatestWarnings(Integer pageSize, Integer offset, Long personId, Integer warnType) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("pageSize", pageSize);
        paramMap.put("offset", offset);
        paramMap.put("personId", personId);
        paramMap.put("warnType", warnType);

        // 调用存储过程（返回数据列表，总条数通过out参数返回）
        List<UnifiedWarningDTO> warningList = warningIndividualMapper.selectPersonLatestWarn(paramMap);

        // 补充人员信息和聚集预警数据
        supplementWarningInfo(warningList);

        // 注意：存储过程返回的totalCount是Integer，需要转为Long
        Integer totalInt = (Integer) paramMap.get("totalCount");
        Long totalCount = totalInt != null ? totalInt.longValue() : 0L;

        return new PageResult<>(warningList, totalCount);
    }

    /**
     * 分批次查询并合并结果（含总条数计算）
     */
    private PageResult<UnifiedWarningDTO> queryByBatches(
            List<Long> personIdList,
            LatestWarnPageReqVO pageVO,
            int batchSize) throws Exception {

        // 拆分ID列表为多个批次
        List<List<Long>> batches = new ArrayList<>();
        for (int i = 0; i < personIdList.size(); i += batchSize) {
            int end = Math.min(i + batchSize, personIdList.size());
            batches.add(personIdList.subList(i, end));
        }

        // 并行查询每个批次的所有数据（不分页，后续统一排序分页）
        List<CompletableFuture<List<UnifiedWarningDTO>>> futures = batches.stream()
                .map(batch -> CompletableFuture.supplyAsync(() -> {
                    String personIds = batch.stream()
                            .map(String::valueOf)
                            .collect(Collectors.joining(","));
                    // 注意：这里需要调用一个能查询"该批次所有数据"的方法（而非分页方法）
                    // 如果原selectByPersonIds方法没有总条数逻辑，可保持不变（仅查询数据）
                    return warningIndividualMapper.selectByPersonIds(personIds);
                }, executorService))
                .toList();

        // 等待所有查询完成并合并结果
        List<UnifiedWarningDTO> allData = new ArrayList<>();
        for (CompletableFuture<List<UnifiedWarningDTO>> future : futures) {
            allData.addAll(future.get());
        }

        // 补充人员信息和聚集预警数据
        supplementWarningInfo(allData);

        // 全局排序（与存储过程保持一致：按sort_time降序，再按id降序）
        allData.sort((a, b) -> {
            if (a.getSortTime() != null && b.getSortTime() != null) {
                int timeCompare = b.getSortTime().compareTo(a.getSortTime());
                if (timeCompare != 0) {
                    return timeCompare;
                }
            }
            return b.getId().compareTo(a.getId());
        });

        // 计算总条数（所有批次合并后的总数据量）
        long totalCount = allData.size();

        // 内存分页
        int offset = (pageVO.getPageNo() - 1) * pageVO.getPageSize();
        int fromIndex = Math.min(offset, allData.size());
        int toIndex = Math.min(offset + pageVO.getPageSize(), allData.size());
        List<UnifiedWarningDTO> pageData = allData.subList(fromIndex, toIndex);

        // 返回包含总条数的分页结果
        return new PageResult<>(pageData, totalCount);
    }

    /**
     * 提取公共逻辑：补充预警信息（人员信息、聚集数据等）
     */
    private void supplementWarningInfo(List<UnifiedWarningDTO> warningList) {
        if (warningList.isEmpty()) {
            return;
        }

        // 提取聚集ID并查询聚集信息
        Set<Long> gatherIdSet = warningList.stream()
                .filter(t -> t.getWarnType() == 2 && t.getGatherId() != null)
                .map(UnifiedWarningDTO::getGatherId)
                .collect(Collectors.toSet());
        Map<Long, WarningGatherDO> gatherMap = new HashMap<>();
        if (!gatherIdSet.isEmpty()) {
            List<WarningGatherDO> warningGatherDOS = gatherMapper.selectByIds(gatherIdSet);
            gatherMap = warningGatherDOS.stream()
                    .collect(Collectors.toMap(WarningGatherDO::getId, Function.identity()));
        }

        // 提取人员ID并批量查询（优化：减少数据库查询次数）
        Set<Long> personIdSet = warningList.stream()
                .map(UnifiedWarningDTO::getPersonId)
                .collect(Collectors.toSet());
        Map<Long, MemberUserDO> userMap = new HashMap<>();
        if (!personIdSet.isEmpty()) {
            QueryWrapper<MemberUserDO> queryWrapper = new QueryWrapper<>();
            queryWrapper.in("id", personIdSet); // "id" 替换为你的表主键字段名
            List<MemberUserDO> userList = memberUserMapper.selectList(queryWrapper);
            userMap = userList.stream()
                    .collect(Collectors.toMap(MemberUserDO::getId, Function.identity()));
        }

        // 补充信息到DTO
        for (UnifiedWarningDTO warningDTO : warningList) {
            warningDTO.setWarnTypeText(WarnTypeEnum.valueOf(warningDTO.getWarnType()).getName());
            // 补充聚集预警人数
            if (warningDTO.getWarnType() == 2) {
                WarningGatherDO gatherDO = gatherMap.get(warningDTO.getGatherId());
                if (gatherDO != null) {
                    warningDTO.setWarnPersonCount(gatherDO.getWarnPersonCount());
                    warningDTO.setGatherAddress(gatherDO.getGatherAddress());
                    warningDTO.setStartTime(gatherDO.getStartTime());
                    warningDTO.setEndTime(gatherDO.getEndTime());
                    warningDTO.setGatherId(gatherDO.getId());
                    warningDTO.setConfigId(Long.parseLong(gatherDO.getConfigId()));
                    warningDTO.setCreateTime(Date.from(gatherDO.getCreateTime().atZone(ZoneId.systemDefault()).toInstant()));
                    warningDTO.setGatherName(gatherDO.getGatherName());
                    warningDTO.setWarningContent("地址【" + gatherDO.getGatherAddress() + "】触发：" + gatherDO.getGatherName() + "预警");
                }
            }

            // 补充人员信息（头像、姓名、证件号）
            MemberUserDO memberUserDO = userMap.get(warningDTO.getPersonId());
            if (memberUserDO != null) {
                warningDTO.setAvatar(memberUserDO.getAvatar());
                warningDTO.setPersonName(memberUserDO.getName());
                String idNumber = memberUserDO.getForeigner() == 1
                        ? "(护照号)" + memberUserDO.getPassportNumber()
                        : "(身份证号)" + memberUserDO.getIdcard();
                warningDTO.setIdNumber(idNumber);
            }
        }
    }

    /**
     * 统计今日不同类型的预警数量
     */
    @Override
    public List<WarningTypeCountDTO> countTodayWarningsByType(String caseId) {
        // 获取案件相关人员ID
        Set<Long> personIdSet = new HashSet<>();
        if(StrUtil.isNotEmpty(caseId)) {
            personIdSet = casePersonRelationService.getCasePersonRelation(caseId);
        }
        if (StrUtil.isNotEmpty(caseId) && personIdSet.isEmpty()) {
            return new ArrayList<>();
        }

        // 将Set转换为逗号分隔的字符串（存储过程要求的格式）
        String personIds = personIdSet.stream()
                .map(String::valueOf)
                .collect(Collectors.joining(","));

        // 调用存储过程查询统计数据
        Map<String, Object> params = new HashMap<>();
        params.put("personIds", personIds); // 传递逗号分隔的人员ID字符串
        // 不需要初始化OUT参数，MyBatis会自动处理

        List<WarningTypeCountDTO> result = warningIndividualMapper.countTodayWarningsByType(params);

        // 获取输出参数（存储过程执行后会自动填充）
        Integer totalPersonCount = (Integer) params.get("totalPersonCount");
        if (totalPersonCount != null) {
            log.info("统计的有效人员总数: {}", totalPersonCount);
        }

        // 补充预警类型名称
        for (WarningTypeCountDTO dto : result) {
            switch (dto.getWarnType()) {
                case 1:
                    dto.setWarnTypeName("围栏预警");
                    break;
                case 2:
                    dto.setWarnTypeName("聚集预警");
                    break;
                case 3:
                    dto.setWarnTypeName("出入杭预警");
                    break;
                case 4:
                    dto.setWarnTypeName("接入预警");
                    break;
                default:
                    dto.setWarnTypeName("未知预警");
            }
        }
        return result;
    }

    /**
     * 统计今日不同类型的预警数量
     */
    @Override
    public List<WarningTypeCountDTO> countHomeTodayWarningsByType() {
        // 过滤我的人员
        List<Long> personIdList = memberUserService.getMyUserIdList();
        if (!adminUserApi.isAdmin() && personIdList.isEmpty()) {
            // 不是管理员 myUserIdList 必须有值
            return new ArrayList<>();
        }
        // 将Set转换为逗号分隔的字符串（存储过程要求的格式）
        String personIds = personIdList.stream()
                .map(String::valueOf)
                .collect(Collectors.joining(","));

        // 调用存储过程查询统计数据
        Map<String, Object> params = new HashMap<>();
        params.put("personIds", personIds);

        List<WarningTypeCountDTO> result = warningIndividualMapper.countTodayWarningsByType(params);

        // 获取输出参数（存储过程执行后会自动填充）
        Integer totalPersonCount = (Integer) params.get("totalPersonCount");
        if (totalPersonCount != null) {
            log.info("统计的有效人员总数: {}", totalPersonCount);
        }

        // 补充预警类型名称
        for (WarningTypeCountDTO dto : result) {
            switch (dto.getWarnType()) {
                case 1:
                    dto.setWarnTypeName("围栏预警");
                    break;
                case 2:
                    dto.setWarnTypeName("聚集预警");
                    break;
                case 3:
                    dto.setWarnTypeName("出入杭预警");
                    break;
                case 4:
                    dto.setWarnTypeName("接入预警");
                    break;
                default:
                    dto.setWarnTypeName("未知预警");
            }
        }
        return result;
    }

}